
#ifndef _TTG_STRUCTURES_RELEASABLE_OBJECT_
#define _TTG_STRUCTURES_RELEASABLE_OBJECT_
#include "Common/Definitions.h"

namespace ttg
{
	namespace structures
	{
		//Wrapper, that remembers about object status (local or global, allocated in stack or heap).
		//If necessary, releases object.
		template <class T>
		class ReleasableWrapper
		{
			private:
				T *value;
				size_t *ref_count;
				bool should_release;
			public:
				//Creates empty wrapper.
				inline ReleasableWrapper()
				{
					value = NULL;
					ref_count = new size_t;
					*ref_count = 1;
					should_release = false;
				}
				//Creates wrapper with requred object.
				inline ReleasableWrapper(T *object, bool should_relese)
				{
					this->value = NULL;
					ref_count = new size_t;
					*ref_count = 1;
					this->should_release = false;
					set(object, should_release);
				}
				//Creates wrapper-copy.
				inline ReleasableWrapper(const ReleasableWrapper &wrapper)
				{
					this->value = wrapper.value;
					this->should_release = wrapper.should_release;
					ref_count = wrapper.ref_count;
					*ref_count += 1;
				}
				//Returns reference to owned object.
				//Can throw ttg::Error().
				inline T &get()
				{
#ifdef TTG_DEBUG
					if (value == NULL)
						throw ttg::Error(ttg::Error::FATAL, "ReleasableWrapper", "get",
										 "Object was not assigned");
#endif
					return *value;
				}
				//Returns direct reference to owned object.
				//Can throw ttg::Error().
				inline T *&getRef()
				{
#ifdef TTG_DEBUG
					if (value == NULL)
						throw ttg::Error(ttg::Error::FATAL, "ReleasableWrapper", "get",
										 "Object was not assigned");
#endif
					return value;
				}
				//Casts to owned object.
				inline operator T &()
				{ return get(); }
				//Sets new object and [optionally] releases previous.
				inline void set(T *object, bool should_release)
				{
					//Releasing previous value.
					if (*ref_count == 1 && this->should_release)
						SAFE_DELETE(this->value);
					//Setting a new one.
					this->should_release = should_release;
					this->value = object;
				}
				//Sets new object and [optionally] releases previous.
				inline void set(T &object, bool should_release)
				{ set(&object, should_release); }
				//Allows to change object state.
				inline void setReleasability(bool value)
				{ should_release = value; }
				inline const ReleasableWrapper<T> &operator = (const ReleasableWrapper<T> &wrapper)
				{
					if (this == &wrapper)
						return *this;
					this->set(wrapper.value, wrapper.should_release);
					if (*this->ref_count == 1)
						SAFE_DELETE(ref_count);
					this->ref_count = wrapper.ref_count;
					*this->ref_count += 1;
					return *this;
				}
				//If necessary, releases owned object.
				~ReleasableWrapper()
				{
					*ref_count -= 1;
					if (*ref_count == 0)
					{
						SAFE_DELETE(ref_count);
						if (should_release)
							SAFE_DELETE(value);
					}
				}
		};
	}
}

#endif
